/*
 * RestHttpHandler.cpp
 *
 *  Created on: 2018年2月9日
 *      Author: terry
 */

#include "RestHttpHandler.h"
#include "HttpUtil.h"
#include "TStringUtil.h"



RestHttpHandler::RestHttpHandler():
	m_auth()
{
}

RestHttpHandler::~RestHttpHandler()
{
}

void RestHttpHandler::setUserStore(HttpUserStorePtr store)
{
	m_auth.setUserStore(store);
}

void RestHttpHandler::setup(const std::string& uri, HttpProcessor proc)
{
	HttpProcessorMap& procMap = m_procTable[uri];
	ProcessorEntry entry(uri, "", proc);
	procMap[entry.method] = entry;
}

void RestHttpHandler::setup(const std::string& uri, const std::string& method, HttpProcessor proc)
{
	HttpProcessorMap& procMap = m_procTable[uri];
	ProcessorEntry entry(uri, method, proc);
	procMap[entry.method] = entry;
}

void RestHttpHandler::setup(const std::string& uri, const char* method, HttpProcessor proc)
{
	std::string m(method);
	setup(uri, m, proc);
}

void RestHttpHandler::setup(const std::string& uri, bool acquireAuth, HttpProcessor proc)
{
	HttpProcessorMap& procMap = m_procTable[uri];
	ProcessorEntry entry(uri, "", proc);
	entry.auth = acquireAuth;
	procMap[entry.method] = entry;
}

void RestHttpHandler::setup(const std::string& uri, const std::string& method, bool acquireAuth, HttpProcessor proc)
{
	HttpProcessorMap& procMap = m_procTable[uri];
	ProcessorEntry entry(uri, method, proc);
	entry.auth = acquireAuth;
	procMap[entry.method] = entry;
}

void RestHttpHandler::setup(const std::string& uri, const char* method, bool acquireAuth, HttpProcessor proc)
{
	std::string m(method);
	setup(uri, m, acquireAuth, proc);
}

bool RestHttpHandler::getToken(HttpRequest& request, std::string& token)
{
	if (HttpUtil::getVar(&request.query, "token", token))
	{
		return true;
	}

	struct mg_str* str = mg_get_http_header(request.message, "Authorization");
	if (str == NULL)
	{
		return false;
	}

	std::string head;
	split(*str, ' ', head, token);

	return !token.empty();
}

bool RestHttpHandler::getUser(HttpRequest& request, std::string& username)
{
	return m_auth.check(request.token, username);
}

bool RestHttpHandler::checkAuth(HttpRequest& request, std::string& jsonText)
{
	if (!getToken(request, request.token))
	{
		setError(401, "need auth", jsonText);
		return false;
	}

	std::string username;
	if (!m_auth.check(request.token, username))
	{
		setError(401, "Unauthorized", jsonText);
		return false;
	}

	return true;
}

int RestHttpHandler::setError(int code, const std::string& message, std::string& jsonText)
{
	jsonText = comn::StringUtil::format("{\"code\":%d, \"message\": \"%s\"}", code, message.c_str());
	return code;
}

size_t RestHttpHandler::split(const mg_str& str, char sep, std::string& head, std::string& tail)
{
	if (str.len == 0)
	{
		return 0;
	}

	if (str.len <= 1)
	{
		head.assign(str.p, str.len);
		return 1;
	}

	size_t pos = -1;
	for (size_t i = 1; i < str.len; ++ i)
	{
		if (str.p[i] == sep)
		{
			pos = i;
			break;
		}
	}

	if (pos == -1)
	{
		head.assign(str.p, str.len);
		return 1;
	}

	head.assign(str.p, pos);
	tail.assign(str.p + pos + 1, str.len - pos - 1);
	return 2;
}

int RestHttpHandler::handleJsonApi(HttpRequest& request, std::string& json)
{
	ProcessorEntry entry;
	if (!findProcessor(request, entry))
	{
		return 404;
	}

	if (entry.auth)
	{
		if (!checkAuth(request, json))
		{
			return 401;
		}
	}

	return entry.processor(request, json);
}

bool RestHttpHandler::findProcessor(HttpRequest& request, ProcessorEntry& entry)
{
	ProcessorTable::const_iterator it = findEntry(request, request.params);
	if (it == m_procTable.end())
	{
		return false;
	}

	const HttpProcessorMap& procMap = it->second;
	std::string method(request.method.p, request.method.len);
	HttpProcessorMap::const_iterator pos = procMap.find(method);
	if (pos == procMap.end())
	{
		pos = procMap.find("");
	}

	if (pos == procMap.end())
	{
		return false;
	}

	entry = pos->second;
	return true;
}

static size_t getSubString(const char* str, size_t len, size_t idx, char sp, std::string& value)
{
	size_t i = idx;
	for (; i < len; i ++)
	{
		if (str[i] == sp)
		{
			break;
		}

		value += str[i];
	}
	return i;
}

bool RestHttpHandler::matchEntry(HttpRequest& request, const std::string& pattern, StringMap& params)
{
	const char SEPERATOR = '/';
	const char COLON = ':';

	size_t i = 0;
	size_t j = 0;

	while (i < pattern.size() && j < request.uri.len)
	{
		std::string name;
		std::string value;

		if (pattern[i] == COLON)
		{
			i = getSubString(pattern.c_str(), pattern.size(), i + 1, SEPERATOR, name);
			j = getSubString(request.uri.p, request.uri.len, j, SEPERATOR, value);

			params[name] = value;
		}
		else if (pattern[i] == request.uri.p[j])
		{
			i ++;
			j ++;
		}
		else
		{
			break;
		}
	}

	return (i >= pattern.size()) && (j >= request.uri.len);
}

RestHttpHandler::ProcessorTable::const_iterator RestHttpHandler::findEntry(HttpRequest& request, StringMap& params)
{
	ProcessorTable::const_iterator it = m_procTable.begin();
	for (; it != m_procTable.end(); ++ it)
	{
		if (matchEntry(request, it->first, params))
		{
			return it;
		}
	}
	return m_procTable.end();
}

int RestHttpHandler::login(HttpRequest& request, std::string& jsonText)
{
	std::string username;
	std::string password;
	if (HttpUtil::getVar(&request.query, "username", username))
	{
		HttpUtil::getVar(&request.query, "password", password);
	}
	else if (HttpUtil::getVar(&request.body, "username", username))
	{
		HttpUtil::getVar(&request.body, "password", password);
	}
	else 
	{
		//
	}

	std::string token;
	if (!m_auth.login(username, password, token))
	{
		setError(401, "Unauthorized", jsonText);
		return 401;
	}

	jsonText = comn::StringUtil::format("{\"username\":\"%s\", \"token\": \"%s\"}", username.c_str(), token.c_str());

	return 0;
}

int RestHttpHandler::logout(HttpRequest& request, std::string& jsonText)
{
	std::string token;
	if (!getToken(request, token))
	{
		setError(401, "Unauthorized", jsonText);
		return 401;
	}

	setError(0, "", jsonText);

	m_auth.logout(token);
	return 0;
}

